package de.hfu.helper;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.MalformedURLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import android.preference.PreferenceManager;
import de.hfu.android.R;
import de.hfu.model.DayPlan;
import de.hfu.model.Dish;

public class DataAccess {

	private static final String DEFAULT_CAMPUS = "Schwenningen";
	public static final String DEFAULT_LANGUAGE = "Deutsch";

	private static DataAccess instance = new DataAccess();

	private static final int MISSING_DAY = Calendar.SUNDAY;

	private Context context;

	private DataAccess() {
		context = null;
	}

	public static DataAccess getInstance(Context context) {
		if (context != null) {
			instance.context = context;
			return instance;
		} else
			return null;
	}

	private void storeDayPlan2ExternalStorage(DayPlan plan, String fileName)
			throws FileNotFoundException, IOException {
		String state = Environment.getExternalStorageState();

		if (state.equals(Environment.MEDIA_MOUNTED)) {
			File dir = context.getExternalFilesDir(null);

			File file = new File(dir.getPath(), fileName);

			if (!file.exists()) {
				file.createNewFile();
			}
			ObjectOutputStream oos = new ObjectOutputStream(
					new FileOutputStream(file));
			oos.writeObject(plan);
		}
	}

	private void storeDayPlan2ExternalStorage(DayPlan plan, String campus,
			String language) throws IOException, ClassNotFoundException {

		String state = Environment.getExternalStorageState();

		if (state.equals(Environment.MEDIA_MOUNTED)) {

			DayPlan prev = loadDayPlanFromExternalStorage(plan.getDate(),
					campus, DEFAULT_LANGUAGE);
			String fileName = new SimpleDateFormat("MM.dd.yyyy").format(plan
					.getDate()) + "_" + campus;
			boolean ck = false;
			// check if new dayPlan has any changes
			if (prev == null || !prev.equals(plan)) {
				ck = true;
			}
			if (!language.equalsIgnoreCase(DEFAULT_LANGUAGE)) {
				if (ck || !prev.isTranslated()) {
					if (!ck)
						plan = prev;
					DataTranslator.getInstance().translateFromGermanToEnglish(
							plan);
					ck = true;
				}
			}

			if (ck)
				storeDayPlan2ExternalStorage(plan, fileName);
		} else {
			throw new IOException("Cannot write to external storage");
		}

	}

	public void storeDayPlan2ExternalStorage(DayPlan plan) {
		try {
			storeDayPlan2ExternalStorage(plan, getCampusName(),
					getDishLanguage());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private DayPlan loadDayPlanFromExternalStorage(Date date, String campus,
			String language) throws IOException, ClassNotFoundException {

		String state = Environment.getExternalStorageState();

		if (state.equals(Environment.MEDIA_MOUNTED)
				|| state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
			File dir = context.getExternalFilesDir(null);

			String fileName = new SimpleDateFormat("MM.dd.yyyy").format(date)
					+ "_" + campus;
			File file = new File(dir.getPath(), fileName);
			if (!file.exists()) {
				return null;
			}
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
					file));
			Object obj = ois.readObject();
			if (obj instanceof DayPlan) {
				DayPlan tmp = (DayPlan) obj;
				if (!language.equalsIgnoreCase(DEFAULT_LANGUAGE)
						&& !tmp.isTranslated()) {
					DataTranslator.getInstance().translateFromGermanToEnglish(
							tmp);
					storeDayPlan2ExternalStorage(tmp, fileName);
				}
				return ((DayPlan) obj);
			}

			return null;
		} else {
			throw new IOException("Cannot read from external storage");
		}
	}

	public DayPlan loadDayPlanFromExternalStorage(Date date) {
		try {
			return loadDayPlanFromExternalStorage(date, getCampusName(),
					getDishLanguage());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	// load all day plans from today
	private ArrayList<DayPlan> loadDayPlansFromExternalStorage(String campus,
			String language) throws IOException, ClassNotFoundException {
		ArrayList<DayPlan> result = null;
		Date date = new Date(System.currentTimeMillis());
		int MILLIS_IN_DAY = 1000 * 60 * 60 * 24;
		boolean ck = false;
		while (true) {
			DayPlan tmp = loadDayPlanFromExternalStorage(date, campus, language);
			if (tmp == null) {
				if (ck)
					break;
				else
					ck = true;
			} else {
				if (result == null)
					result = new ArrayList<DayPlan>();
				result.add(tmp);
			}

			date = new Date(date.getTime() + MILLIS_IN_DAY);
		}
		return result;
	}

	public ArrayList<DayPlan> loadDayPlansFromExternalStorage() {
		try {
			return loadDayPlansFromExternalStorage(getCampusName(),
					getDishLanguage());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	// load all day plans
	private ArrayList<DayPlan> loadAllDayPlansFromExternalStorage(
			final String campus, String language) throws IOException,
			ClassNotFoundException {
		ArrayList<DayPlan> result = null;

		String state = Environment.getExternalStorageState();

		if (state.equals(Environment.MEDIA_MOUNTED)
				|| state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
			File dir = context.getExternalFilesDir(null);

			FilenameFilter filter = new FilenameFilter() {

				@Override
				public boolean accept(File dir, String name) {
					return name.indexOf(campus) > -1;
				}
			};
			File[] arr = dir.listFiles(filter);
			Arrays.sort(arr, new Comparator<File>() {

				@Override
				public int compare(File lhs, File rhs) {
					return lhs.getName().compareTo(rhs.getName());
				}
			});

			for (File x : arr) {
				if (x.getName().indexOf(campus) > -1) {
					ObjectInputStream ois = new ObjectInputStream(
							new FileInputStream(x));
					Object obj = ois.readObject();
					if (obj instanceof DayPlan) {
						if (result == null)
							result = new ArrayList<DayPlan>();
						result.add((DayPlan) obj);
					}
				}
			}
			return result;
		} else {
			throw new IOException("Cannot read from external storage");
		}
	}

	public ArrayList<DayPlan> loadAllDayPlansFromExternalStorage() {
		try {
			return loadAllDayPlansFromExternalStorage(getCampusName(),
					getDishLanguage());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	// load DayPlans in range of Dates
	private ArrayList<DayPlan> loadDayPlansInRangeFromExternalStorage(
			Date from, Date to, String campus, String language)
			throws IOException, ClassNotFoundException {
		ArrayList<DayPlan> result = new ArrayList<DayPlan>();
		Date date = from;
		boolean ck = false;
		Calendar cal = Calendar.getInstance();
		while (true) {
			cal.setTime(date);
			if (cal.get(Calendar.DAY_OF_WEEK) != MISSING_DAY) {
				DayPlan tmp = loadDayPlanFromExternalStorage(date, campus,
						language);
				if (tmp != null) {
					ck = true;
					result.add(tmp);
				} else {
					result.add(new DayPlan(date));
				}
			}
			date = new Date(date.getTime() + Helper.MILLIS_IN_DAY);
			if (date.compareTo(to) > 0 && !Helper.isEqual(date, to)) {
				break;
			}
		}
		if (!ck)
			result = null;
		return result;
	}

	public ArrayList<DayPlan> loadDayPlansInRangeFromExternalStorage(Date from,
			Date to) {
		try {
			return loadDayPlansInRangeFromExternalStorage(from, to,
					getCampusName(), getDishLanguage());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	// load day plans for this week from External Storage
	private ArrayList<DayPlan> loadDayPlansOfThisWeekFromExternalStorage(
			String campus, String language) throws IOException,
			ClassNotFoundException {

		Calendar cal = Calendar.getInstance();
		int week = cal.get(Calendar.WEEK_OF_MONTH);
		cal.set(Calendar.WEEK_OF_YEAR, week);
		cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
		Date from = new Date(cal.getTimeInMillis());
		Date to = new Date(cal.getTimeInMillis() + Helper.MILLIS_IN_DAY * 5);

		return loadDayPlansInRangeFromExternalStorage(from, to, campus,
				language);
	}

	public ArrayList<DayPlan> loadDayPlansOfThisWeekFromExternalStorage() {
		try {
			return loadDayPlansOfThisWeekFromExternalStorage(getCampusName(),
					getDishLanguage());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	// load day plans for a week (6 days) from External Storage
	private ArrayList<DayPlan> loadDayPlansOfAWeekFromExternalStorage(
			String campus, String language) throws IOException,
			ClassNotFoundException {

		Date from = new Date(System.currentTimeMillis());
		Date to;
		Calendar cal = Calendar.getInstance();
		cal.setTime(from);
		if (cal.get(Calendar.DAY_OF_WEEK) == MISSING_DAY) {
			from = new Date(System.currentTimeMillis() + Helper.MILLIS_IN_DAY);
		}
		to = new Date(System.currentTimeMillis() + Helper.MILLIS_IN_DAY * 5);

		return loadDayPlansInRangeFromExternalStorage(from, to, campus,
				language);
	}

	public ArrayList<DayPlan> loadDayPlansOfAWeekFromExternalStorage() {
		try {
			return loadDayPlansOfAWeekFromExternalStorage(getCampusName(),
					getDishLanguage());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	// update data from the Internet
	private void updateFromTheInternet(String campus, String language)
			throws ParseException, IOException, ClassNotFoundException {
		DataParser parser = DataParser.getInstance();
		ArrayList<DayPlan> plans = null;

		plans = parser.getDataFromInternet(campus);

		for (DayPlan plan : plans) {
			storeDayPlan2ExternalStorage(plan, campus, language);
		}

	}

	public void updateFromTheInternet() {
		try {
			updateFromTheInternet(getCampusName(), getDishLanguage());
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// update dish comment & rating
	private void updateDish(Dish dish, String campus, String language)
			throws IOException, ClassNotFoundException {
		DayPlan dayPlan = loadDayPlanFromExternalStorage(dish.getDate(),
				campus, language);

		for (Dish x : dayPlan.getDishes())
			if (x.equals(dish)) {
				x.setComment(dish.getComment());
				x.setRating(dish.getRating());
				x.setPostedComment(dish.isPostedComment());
				x.setPostedRating(dish.isPostedRating());
				break;
			}
		String fileName = new SimpleDateFormat("MM.dd.yyyy").format(dayPlan
				.getDate()) + "_" + campus;
		storeDayPlan2ExternalStorage(dayPlan, fileName);
	}

	public void updateDish(Dish dish) {
		try {
			updateDish(dish, getCampusName(), getDishLanguage());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public String getCampusName() {
		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(context);
		String campus = prefs.getString("campus", DEFAULT_CAMPUS);
		// android.util.Log.v("campus", campus);
		return campus;
	}

	public String getDishLanguage() {
		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(context);
		String language = prefs.getString("dish_language",
				context.getString(R.string.deutsch));
		// android.util.Log.v("language", language);
		return language;
	}
}
